package label_service

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"net/http"
	"peilian-api/app/global/common"
	"peilian-api/app/global/databases/tables"
	"peilian-api/app/global/log"
	"peilian-api/app/global/variable"
	"peilian-api/utils/tools"
	"strings"
)

type CreateLabelReq struct {
	JobName string `json:"job_name"`
}

func CreateLabel(c context.Context, req CreateLabelReq) (string, error) {
	// 先查询是否已经存在
	db := tables.CustomLabel{}
	label := db.GetLabelByName(req.JobName)
	if label != nil && label.ID != 0 {
		return "job has bin add", nil
	}
	label = &tables.CustomLabel{
		JobName: req.JobName,
	}
	err := variable.DB.MySQL.Model(db).Create(label).Error
	if err != nil {
		log.Logger.ErrorF(c, fmt.Sprintf("insert label error: %v", err))
		return "", err
	}
	// 发送任务
	err = addTask(c, req.JobName)
	if err != nil {
		return "", err
	}

	return "success", nil
}

func addTask(ctx context.Context, jobName string) error {
	data := map[string]string{
		"job_name": jobName,
	}
	reqJsonData, _ := json.Marshal(data)
	resp, err := http.Post(
		variable.Config.Services.CustomLabelAdd,
		"application/json",
		bytes.NewReader(reqJsonData),
	)
	if err != nil {
		log.Logger.ErrorF(ctx, "添加任务调用失败: "+err.Error())
		return err
	}
	if resp != nil {
		defer resp.Body.Close()
	}
	type respStruck struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			TaskId      string `json:"task_id"`
			QueueLength int    `json:"queue_length"`
		} `json:"data"`
	}
	res := respStruck{}
	err = tools.ReadResponseBody(resp, &res)
	if err != nil {
		log.Logger.ErrorF(ctx, "添加任务失"+err.Error())
		return err
	}
	updateData := map[string]interface{}{
		"task_id": res.Data.TaskId,
		"status":  2,
	}
	err = variable.DB.MySQL.Model(tables.CustomLabel{}).Where("job_name = ?", jobName).Updates(updateData).Error
	if err != nil {
		log.Logger.ErrorF(ctx, "update db error"+err.Error())
		return err
	}
	return nil
}

func getTask(ctx context.Context, label *tables.CustomLabel) (map[string]interface{}, error) {
	data := map[string]string{
		"task_id": label.TaskId,
	}
	reqJsonData, _ := json.Marshal(data)
	resp, err := http.Post(
		variable.Config.Services.CustomLabelGet,
		"application/json",
		bytes.NewReader(reqJsonData),
	)
	if err != nil {
		log.Logger.ErrorF(ctx, "获取任务调用失败: "+err.Error())
		return nil, err
	}
	if resp != nil {
		defer resp.Body.Close()
	}
	type respStruck struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			Status string                 `json:"status"`
			Output map[string]interface{} `json:"output"`
		} `json:"data"`
	}
	res := respStruck{}
	err = tools.ReadResponseBody(resp, &res)
	if err != nil {
		log.Logger.ErrorF(ctx, "获取任务序列化失败:"+err.Error())
		return nil, err
	}
	switch res.Data.Status {
	case "NOT_START", "ONGOING":
		return nil, nil
	case "REMOVE":
		e := addTask(ctx, label.JobName)
		return nil, e
	case "FINISH":
		labelData, _ := json.Marshal(res.Data.Output)
		updateData := map[string]interface{}{
			"content": string(labelData),
			"status":  3,
		}
		err = variable.DB.MySQL.Model(tables.CustomLabel{}).Where("id = ?", label.ID).Updates(updateData).Error
		if err != nil {
			log.Logger.ErrorF(ctx, "update db error"+err.Error())
			return nil, err
		}
		label.Content = string(labelData)
		return res.Data.Output, nil
	default:
		e := addTask(ctx, label.JobName)
		return nil, e
	}
}

func pushLabel(ctx context.Context, label *tables.CustomLabel) error {
	labelData := make(map[string]interface{})
	err := json.Unmarshal([]byte(label.Content), &labelData)
	if err != nil || label.Content == "" {
		log.Logger.ErrorF(ctx, fmt.Sprintf("label content error:%+v content:%s", err, label.Content))
		return errors.New("label content error")
	}

	data := map[string]interface{}{
		"job_name":  label.JobName,
		"word_dict": labelData,
	}
	reqJsonData, _ := json.Marshal(data)
	resp, err := http.Post(
		variable.Config.Services.CustomLabelPush,
		"application/json",
		bytes.NewReader(reqJsonData),
	)
	if err != nil {
		log.Logger.ErrorF(ctx, "获取任务调用失败: "+err.Error())
		return err
	}
	if resp != nil {
		defer resp.Body.Close()
	}
	type respStruck struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			Status string `json:"status"`
		} `json:"data"`
	}
	res := respStruck{}
	err = tools.ReadResponseBody(resp, &res)
	if err != nil {
		log.Logger.ErrorF(ctx, "获取任务序列化失败:"+err.Error())
		return err
	}
	if res.Data.Status == "ok" {
		// 将对应的职位改成web
		careerUpdate := map[string]interface{}{
			"report_type": "web",
		}
		err = variable.DB.MySQL.Model(tables.Career{}).Where("name = ?", label.JobName).Updates(careerUpdate).Error
		if err != nil {
			log.Logger.ErrorF(ctx, fmt.Sprintf("update db error:%+v", err))
			return err
		}
		updateData := map[string]interface{}{
			"status": 4,
		}
		err = variable.DB.MySQL.Model(tables.CustomLabel{}).Where("id = ?", label.ID).Updates(updateData).Error
		if err != nil {
			log.Logger.ErrorF(ctx, "update db error:"+err.Error())
			return err
		}
		return nil
	}
	return nil
}

func AsyncGetTask() {
	ctx := &gin.Context{}
	uuidStr := strings.ReplaceAll(uuid.NewString(), "-", "")
	ctx.Set(common.TraceKey, context.WithValue(context.Background(), common.TraceKey, &log.Trace{TraceId: uuidStr, SpanId: uuidStr, Caller: ""}))

	var labels []*tables.CustomLabel
	err := variable.DB.MySQL.Model(&tables.CustomLabel{}).Find(&labels).Error
	if err != nil {
		log.Logger.ErrorF(ctx, "select labels error", err.Error())
		return
	}
	for _, l := range labels {
		switch l.Status {
		case 1:
			err = addTask(ctx, l.JobName)
			log.Logger.InfoF(ctx, fmt.Sprintf("add task result:%+v", err))
		case 2:
			result, err := getTask(ctx, l)
			log.Logger.InfoF(ctx, fmt.Sprintf("get task result:%+v", err))
			if result != nil {
				err = pushLabel(ctx, l)
				log.Logger.InfoF(ctx, fmt.Sprintf("push task result:%+v", err))
			}
		case 3:
			err = pushLabel(ctx, l)
			log.Logger.InfoF(ctx, fmt.Sprintf("push task result:%+v", err))
		case 4:
			content := make(map[string]struct{})
			_ = json.Unmarshal([]byte(l.Content), &content)
			if len(content) == 0 {
				log.Logger.WarnF(ctx, fmt.Sprintf("content result is null:%s", l.Content))
				err = addTask(ctx, l.JobName)
				log.Logger.InfoF(ctx, fmt.Sprintf("add task result:%+v", err))
				updateData := map[string]interface{}{
					"status": 2,
				}
				err = variable.DB.MySQL.Model(tables.CustomLabel{}).Where("id = ?", l.ID).Updates(updateData).Error
				if err != nil {
					log.Logger.ErrorF(ctx, "update db error"+err.Error())
				}
			}
		}
	}
}
